﻿namespace InkType.ViewModel

open InkType.ViewModel.Xml
open System
open System.Collections.Generic
open System.Globalization


type CharacterBlockID =
    | RangeBlockID of first : int * last : int
    | ListBlockID of id : Guid

    override x.ToString () =
        sprintf "%A" x


type CharacterBlock =
    | RangeBlock of first : int * last : int * name : string
    | ListBlock of id : Guid * name : string * value : string

    member x.Name =
        match x with
        | RangeBlock (_, _, name) -> name
        | ListBlock (_, name, _) -> name

    member x.Characters =
        match x with
        | RangeBlock (first, last, _) ->
            seq { for i in first .. last do yield char i }
        | ListBlock (_, _, value) ->
            value :> _

    member x.CharacterCount =
        match x with
        | RangeBlock (first, last, _) -> last - first + 1
        | ListBlock (_, _, value) -> value.Length

    member x.ID =
        match x with
        | RangeBlock (first, last, _) -> RangeBlockID (first, last)
        | ListBlock (id, _, _) -> ListBlockID id

    static member FromXml xdoc =
        let XDoc (root) as xdoc = xdoc
        match root with
        | XElem ("Blocks", _, children) ->
            children
            |> Seq.choose (
                function
                | XElem ("Range", attrs, _) ->
                    let first =
                        XAttrs.tryGet "First" attrs
                        |> Option.bind (fun s -> try Some (int s) with | _ -> None)
                    let last =
                        XAttrs.tryGet "Last" attrs
                        |> Option.bind (fun s -> try Some (int s) with | _ -> None)
                    let name =
                        XAttrs.tryGet "Name" attrs
                    match first, last, name with
                    | Some first, Some last, Some name when last >= first -> Some (RangeBlock (first, last, name))
                    | _, _, _ -> None

                | XElem ("List", attrs, _) ->
                    let id =
                        XAttrs.tryGet "ID" attrs
                        |> Option.bind (Guid.TryParse >> Option.ofByRefBool)
                    let name =
                        XAttrs.tryGet "Name" attrs
                    let value =
                        XAttrs.tryGet "Value" attrs
                    match id, name, value with
                    | Some id, Some name, Some value -> Some (ListBlock (id, name, value))
                    | _, _, _ -> None

                | XElem (_, _, _) ->
                    None)

        | XElem (_, _, _) ->
            Seq.empty


